use std::path::Path;

use rand::prelude::*;
use tokio::fs;
use tokio::io::{self, AsyncReadExt, AsyncSeekExt, AsyncWriteExt};

use crate::{Error, Result};

/// 节点id.
///
/// 为uuid.
#[derive(Clone)]
pub struct NodeId {
    uuid: uuid::Uuid,
}

impl NodeId {
    /// 从文件读取.
    ///
    /// path为文件目录,如存在则读取,如不存在则生成后保存.
    /// 根据随机数生成,所以可能会重复.
    pub async fn load<P: AsRef<Path>>(path: P) -> Result<Self> {
        if let Some(dir) = path.as_ref().parent() {
            fs::create_dir_all(dir).await.map_err(Error::from)?;
        }

        let mut file = fs::OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .open(path.as_ref())
            .await
            .map_err(Error::from)?;

        file.seek(io::SeekFrom::Start(0))
            .await
            .map_err(Error::from)?;
        let mut s = String::new();
        file.read_to_string(&mut s).await.map_err(Error::from)?;

        if s.is_empty() {
            let mut rng = thread_rng();
            let uuid = uuid::Builder::from_u128(rng.gen())
                .set_variant(uuid::Variant::RFC4122)
                .set_version(uuid::Version::Random)
                .build();

            let s = uuid.to_hyphenated().to_string();
            file.write_all(s.as_bytes()).await.map_err(Error::from)?;
            file.sync_all().await.map_err(Error::from)?;

            Ok(Self { uuid })
        } else {
            let uuid = uuid::Uuid::parse_str(s.as_str()).map_err(Error::from)?;

            Ok(Self { uuid })
        }
    }
}

impl std::fmt::Display for NodeId {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        let mut buf = [0; 36];
        write!(f, "{}", self.uuid.to_simple_ref().encode_lower(&mut buf))
    }
}
